//Package compiler implements a flow execution engine defined by map.
package compiler

import (
	"fmt"
	"gitee.com/engino/flow/impl/defn"
	"gitee.com/engino/flow/process"
)

//Flow the flow definition.
type Flow struct {
	Name    string
	NodeMap map[string]process.Node
}

//GetName get the name of the flow.
func (f *Flow) GetName() string {
	return f.Name
}

//GetNode get the node definition.
func (f *Flow) GetNode(name string) process.Node {
	return f.NodeMap[name]
}

//Context the context.
type Context struct {
	VariableMap map[string]process.Variable
}

//Get get the variable of the context.
func (ctx *Context) Get(name string) process.Variable {
	return ctx.VariableMap[name]
}

//Eval evaluate the given expression.
func (ctx *Context) Eval(expr string) interface{} {
	return true
}

//Variable the simple variable.
type Variable struct {
	*defn.Variable
	value interface{}
}

//GetName get the name of the variable.
func (v *Variable) GetName() string {
	return v.Name
}

//GetValue get the value.
func (v *Variable) GetValue() interface{} {
	return v.value
}

//SetValue set the value.
func (v *Variable) SetValue(val interface{}) error {
	v.value = val
	return nil
}

//Instance the instance.
type Instance struct {
	ctx   *Context
	def   *Flow
	state process.State
}

//GetContext get the context of the process instance.
func (inst *Instance) GetContext() process.Context {
	return inst.ctx
}

//GetFlow get the flow definition of the process instance.
func (inst *Instance) GetFlow() process.Flow {
	return inst.def
}

//GetState get the state of the instance.
func (inst *Instance) GetState() process.State {
	return inst.state
}

//EndNode the end node.
type EndNode struct{}

//GetName get the name of the end node.
func (n *EndNode) GetName() string {
	return "End"
}

//Execute to set the state to `completed`.
func (n *EndNode) Execute(inst process.Instance) error {
	process.Complete(inst)
	return nil
}

//Compile compile the definition into a runnabl instance.
func Compile(def *defn.FlowDef) process.Instance {
	variableMap := make(map[string]process.Variable)
	for _, v := range def.Variables {
		variableMap[v.Name] = &Variable{v, v.DefaultValue}
	}

	nodeMap := make(map[string]process.Node)
	for _, a := range def.Assignments {
		nodeMap[a.Name] = &AssignmentNode{a}
	}
	for _, d := range def.Decisions {
		nodeMap[d.Name] = &DecisionNode{d}
	}
	nodeMap[""] = new(EndNode)

	inst := &Instance{
		&Context{VariableMap: variableMap},
		&Flow{fmt.Sprintf("%s@%s", def.Name, def.Version), nodeMap},
		process.NewState(process.StateNameRunnable, def.StartElement),
	}

	return inst
}
